﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/*
 * 结合RenderTexture使用，使用了Git effect/studyshader/ex12_5的代码和shader
 * [重要的讲解文章]RenderTexture详解 如图dem02_image01.png https://www.jianshu.com/p/fa73c0f6762d
 * 
 * 
 * CommandBuffer默认修改了默认的RenderTexture(也就是game窗口),
 * 如果调用CommandBuffer.SetRenderTarget().它就会把渲染内容附在那个RT上,
 * 而不会对Game窗口的渲染内容有任何影响.可以通过以下方式修改到game窗口
 * 
 * 可以看出来CommandBuffer这个东西自由度很高,根据CameraEvent ,
 * 它可以穿插在任意步骤之中,如果插在everything之后,
 * 它还能用作屏幕后处理.看到这大概可以猜到 CommandBuffer其实对应的是OpenGL中的Push Buffer,
 * 可以理解成Unity使用PushBuffer构成了固定渲染管线,
 * 同时封装了CommandBuffer供开发者自定义一些渲染指令. 
 * 另外,Unity2019.3中,自定义渲染管线SRP已经脱离预览版,
 * 其实就是允许开发者使用CommandBuffer自己构建一套渲染管线.
 * 
 * 总结CommandBuffer的用法,就是三个词: 
 * when:何时绘制?  由CameraEvent决定渲染时机.
 * how :如何绘制?  cb.DrawRenderer()  cb.DrawMesh()  cb.Blit()  不同的API绘制的内容也不同,再传入指定的材质球,指定的RT即可
 * where:在哪里绘制? 
 * 以上演示的都是在默认的Transform绘制,如果想在别的地方绘制,调用Matrix4x4.SetTRS() 
 * 
 * 
 * 
 * 
 * 
 */
namespace Example02_Demo02
{
	public class Demo02 : MonoBehaviour
	{
        [Range(0.0f, 0.9f)]
        public float blurAmount = 0.5f;

        public RenderTexture rt;
        public Material material;
		// Use this for initialization
		void Start()
		{

		}
        private void OnDisable()
        {
            DestroyImmediate(rt);
        }

        //挂在摄像机的物体才能收到该回调
        public void OnRenderImage(RenderTexture source, RenderTexture destination)
        {
            if (material != null)
            {
                if (rt == null || rt.width != source.width || rt.height != source.height)
                {//在确认材质可用后，我们首先判断用于混合图像的accumulationTexture是否满足条件。
                 //我们不仅判断它是否为空，还判断它是否与当前的屏幕分辨率相等，
                 //如果不满足，就说明我们需要重新创建一个适合于当前分辨率的accumulationTexture变量。创建完毕后，由于我们会自己控制该变量的销毁，
                 //因此可以把它的hideFlags设置为HideFlags.HideAndDontSave，这意味着这个变量不会显示在Hierarchy中，也不会保存到场景中。
                 //然后，我们使用当前的帧图像初始化accumulationTexture（使用Graphics.Blit(src,accumulationTexture)代码）。
                    DestroyImmediate(rt);
                    rt = new RenderTexture(source.width, source.height, 0);
                    rt.hideFlags = HideFlags.HideAndDontSave;
                    Graphics.Blit(source, rt);
                }

                rt.MarkRestoreExpected();      //当得到了有效的accumulationTexture变量后，我们调用了accumulationTexture.MarkRestoreExpected函数来表明我们需要进行一个渲染纹理的恢复操作。
                                               //恢复操作（restore operation）发生在渲染到纹理而该纹理又没有被提前清空或销毁的情况下。//？？？？不懂
                                               //在本例中，我们每次调用OnRenderImage时都需要把当前的帧图像和accumulationTexture中的图像混合，accumulationTexture纹理不需要提前清空，因为它保存了我们之前的混合结果。
                                               //然后，我们将参数传递给材质，并调用Graphics.Blit(src,accumulationTexture,material)把当前的屏幕图像src叠加到accumulationTexture中。
                                               //最后使用Graphics.Blit(accumulationTexture,dest)把结果显示到屏幕上。


                material.SetFloat("_BlurAmount", 1.0f - blurAmount);

                Graphics.Blit(source, rt, material);
                Graphics.Blit(rt, destination);//将rt的数据赋予destination,这里的destination指的就是game窗口的FBO
            }
            else
            {
                Graphics.Blit(source, destination);
            }
           
        }
    }
}
